#include "pt_config.h"
#include "device.h"
#include "ptdebug.h"

#ifdef PT_USING_DEVICE

//#define setbit(x,y)         x|=(1<<y)
//#define clrbit(x,y)         x&=~(1<<y)
//#define reversebit(x,y)     x^=(1<<y)
//#define getbit(x,y)         ((x) >> (y)&1)

pt_list_t device_list_head = {
    .next = &device_list_head,
    .prev = &device_list_head,
};

/**
 * This function registers a device driver with specified name.
 *
 * @param dev the pointer of device driver structure
 * @param name the device driver's name
 * @param flags the capabilities flag of device
 *
 * @return the error code, PT_EOK on initialization successfully.
 */
int32_t pt_device_register(pt_device_t dev,
                            uint8_t     device_id,
                            int16_t flags)
{
    if (dev == PT_NULL){
        return -PT_ERROR;
    }


//    if (pt_device_find(name) != PT_NULL)
//        return -PT_ERROR;
    if( pt_device_find(device_id) != PT_NULL){
        return -PT_ERROR;       //设备id已被使用
    }


    dev->flag = flags;
    dev->ref_count = 0;
    dev->open_flag = 0;
    dev->device_id = device_id;

    pt_list_init(&dev->d_list);

    pt_list_insert_after(&device_list_head, &dev->d_list);
    //    pt_device_use(device_id);

    return PT_EOK;
}
/**
 * This function removes a previously registered device driver
 *
 * @param dev the pointer of device driver structure
 *
 * @return the error code, PT_EOK on successfully.
 */
int32_t pt_device_unregister(pt_device_t dev)
{
    pt_list_remove(&dev->d_list);
    return PT_EOK;
}

/**
 * This function finds a device driver by device id.
 *
 * @param device_id the device driver's id
 *
 * @return 找到则返回设备指针, 否则返回空指针
 */
pt_device_t pt_device_find(uint8_t device_id)
{
    pt_list_t *find_node = &device_list_head;         //从列表头开始找
    find_node = find_node->next;

    do{
        //找到该节点的头, 即设备实例头
        pt_device_t find_device = pt_list_entry(find_node, struct pt_device, d_list);

        //找到该id已被使用, 返回该设备指针
        if ( device_id == find_device->device_id){
            return find_device;
        }

		find_node = find_node->next;
    }while(find_node != &device_list_head);

    return PT_NULL;

}


/**
 * This function will initialize the specified device
 *
 * @param dev the pointer of device driver structure
 *
 * @return the result
 */
int32_t pt_device_init(pt_device_t dev)
{
    PT_ASSERT(dev != PT_NULL);

    int32_t result = PT_EOK;

    /* get device init handler */
    // 调用注册设备时注册的初始化函数
    if (dev->init != PT_NULL){
        //若设备未激活
        if (!(dev->flag & PT_DEVICE_FLAG_ACTIVATED)){
            result = dev->init(dev);
            if (result != PT_EOK){

            }else{
                dev->flag |= PT_DEVICE_FLAG_ACTIVATED;
            }
        }
    }

    return result;
}

/**
 * This function will open a device
 *
 * @param dev the pointer of device driver structure
 * @param oflag the flags for device open
 *
 * @return the result
 */
int32_t pt_device_open ( pt_device_t dev, uint16_t oflag )
{
    PT_ASSERT(dev != PT_NULL);

	int32_t result = PT_EOK;

	// 未初始化, 返回无资源, 应先调用初始化函数
	if ( ! ( dev->flag & PT_DEVICE_FLAG_ACTIVATED ) ) {
        return -PT_EEMPTY;
	}

	/* device is a stand alone device and opened */
	if ( ( dev->flag & PT_DEVICE_FLAG_STANDALONE ) &&
		 ( dev->open_flag & PT_DEVICE_OFLAG_OPEN ) ) {
		return -PT_EBUSY;
	}

	// 调用注册设备时注册的打开函数
	if ( dev->open != PT_NULL ) {
		result = dev->open ( dev, oflag );

	} else {
		/* set open flag */
		dev->open_flag = ( oflag & PT_DEVICE_OFLAG_MASK );
	}

	/* set open flag */
	if ( result == PT_EOK || result == -PT_ENOSYS ) {
		dev->open_flag |= PT_DEVICE_OFLAG_OPEN;

		dev->ref_count++;
		/* don't let bad things happen silently. If you are bitten by this assert,
		 * please set the ref_count to a bigger type. */
        PT_ASSERT ( dev->ref_count != 0 );
	}

	return result;
}


/**
 * This function will close a device
 *
 * @param dev the pointer of device driver structure
 *
 * @return the result
 */
int32_t pt_device_close ( pt_device_t dev )
{
	int32_t result = PT_EOK;

	if ( dev->ref_count == 0 ) {
		return -PT_ERROR;
	}

	dev->ref_count--;

	if ( dev->ref_count != 0 ) {
		return PT_EOK;
	}

	/* call device close interface */
	if ( dev->close != PT_NULL ) {
		result = dev->close ( dev );
	}

	/* set open flag */
	if ( result == PT_EOK || result == -PT_ENOSYS ) {
		dev->open_flag = PT_DEVICE_OFLAG_CLOSE;
	}

	return result;
}


/**
 * This function will read some data from a device.
 *
 * @param dev the pointer of device driver structure
 * @param pos the position of reading
 * @param buffer the data buffer to save read data
 * @param size the size of buffer
 *
 * @return the actually read size on successful, otherwise negative returned.
 *
 * @note since 0.4.0, the unit of size/pos is a block for block device.
 */
uint32_t pt_device_read ( pt_device_t dev,
						  int32_t    pos,
						  void    *   buffer,
						  uint32_t   size )
{

	if ( dev->ref_count == 0 ) {
		return 0;
	}

	/* call device read interface */
	if ( dev->read != PT_NULL ) {
		return dev->read ( dev, pos, buffer, size );
	}

	return 0;
}


/**
 * This function will write some data to a device.
 *
 * @param dev the pointer of device driver structure
 * @param pos the position of written
 * @param buffer the data buffer to be written to device
 * @param size the size of buffer
 *
 * @return the actually written size on successful, otherwise negative returned.
 *
 * @note since 0.4.0, the unit of size/pos is a block for block device.
 */
uint32_t pt_device_write ( pt_device_t dev,
						   int32_t    pos,
						   const void * buffer,
						   uint32_t   size )
{

	if ( dev->ref_count == 0 ) {
		return 0;
	}

	/* call device write interface */
	if ( dev->write != PT_NULL ) {
		return dev->write ( dev, pos, buffer, size );
	}

	return 0;
}

/**
 * This function will perform a variety of control functions on devices.
 *
 * @param dev the pointer of device driver structure
 * @param cmd the command sent to device
 * @param arg the argument of command
 *
 * @return the result
 */
PT_USED static int32_t pt_device_control ( pt_device_t dev, int cmd, void * arg )
{
	/* call device write interface */
	if ( dev->control != PT_NULL ) {
		return dev->control ( dev, cmd, arg );
	}

	return -PT_ENOSYS;
}

/**
 * This function will set the reception indication callback function. This callback function
 * is invoked when this device receives data.
 *
 * @param dev the pointer of device driver structure
 * @param rx_ind the indication callback function
 *
 * @return PT_EOK
 */
int32_t
pt_device_set_rx_indicate ( pt_device_t dev,
							int32_t ( *rx_ind ) ( pt_device_t dev, uint32_t size ) )
{
    PT_ASSERT(dev != PT_NULL);
	dev->rx_indicate = rx_ind;

	return PT_EOK;
}

/**
 * This function will set the indication callback function when device has
 * written data to physical hardware.
 *
 * @param dev the pointer of device driver structure
 * @param tx_done the indication callback function
 *
 * @return PT_EOK
 */
PT_USED static int32_t pt_device_set_tx_complete ( pt_device_t dev,
							int32_t ( *tx_done ) ( pt_device_t dev, void * buffer ) )
{
	dev->tx_complete = tx_done;

	return PT_EOK;
}

#endif //PT_USING_DEVICE
